package com.chen10.gobang.service.impl;

import com.chen10.gobang.common.constants.GameConstants;
import com.chen10.gobang.common.constants.RedisKeyConstants;
import com.chen10.gobang.common.constants.WsEventConstants;
import com.chen10.gobang.common.enums.game.RoomPlayerStatus;
import com.chen10.gobang.common.enums.response.ErrorEnum;
import com.chen10.gobang.common.enums.game.RoomStatusEnums;
import com.chen10.gobang.common.enums.game.UserOnlineStatus;
import com.chen10.gobang.common.exception.BusinessException;
import com.chen10.gobang.common.utils.RedisUtil;
import com.chen10.gobang.common.utils.UserThreadLocal;
import com.chen10.gobang.mapper.UserMapper;
import com.chen10.gobang.model.dto.FightInviteAcceptDTO;
import com.chen10.gobang.model.dto.FightInviteDTO;
import com.chen10.gobang.model.dto.PlayerReadyDTO;
import com.chen10.gobang.model.vo.PlayerInfoVO;
import com.chen10.gobang.model.vo.UserInfoVO;
import com.chen10.gobang.service.FightService;
import com.chen10.gobang.ws.WebSocketHandler;
import com.chen10.gobang.ws.pojo.Room;
import com.chen10.gobang.ws.pojo.WsMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Service
public class FightServiceImpl implements FightService {

    @Resource
    private WebSocketHandler webSocketHandler;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private UserMapper userMapper;

    /**
     * 创建对战房间
     *
     * @return
     */
    @Override
    public Room createRoom() {

        Integer userId = UserThreadLocal.getUserId();
        //判断是否已经有房间了

        if (redisUtil.get(GameConstants.ROOM_KEY_BY_USERID_PREFIX+ userId) != null) {
            throw new BusinessException(ErrorEnum.ROOM_ALREADY_CREATED);
        }

        String roomId = GameConstants.ROOMID_PREFIX + userId; //ROOM_1  ROOM_+userId

        PlayerInfoVO playerInfoVO = getPlayerInfo(userId);
        playerInfoVO.setIfRoomMaster(true);
        playerInfoVO.setReadyStatus(RoomPlayerStatus.READY.getCode());

        Room room = new Room();
        room.setRoomId(roomId);
        room.setPlayerInfoList(new ArrayList<>(Arrays.asList(playerInfoVO))); // 使用新的可变列表
        room.setStatus(RoomStatusEnums.WAITING.getDesc());


        //将房间信息存到redis
        redisUtil.set(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId, room); //key: room: <roomId>
        //更新缓存用户状态
        redisUtil.set(RedisKeyConstants.REDIS_USER_STATUS_KEY_PREFIX + userId, UserOnlineStatus.INROOM.getDesc());

        //广播给好友，更新状态
        WsMessage<String> wsMessage = new WsMessage<>();
        wsMessage.setEvent(WsEventConstants.USER_CREATE_ROOM_EVENT);
        wsMessage.setTimestamp(System.currentTimeMillis());
        webSocketHandler.broadcastToFriends(userId, wsMessage);

        return room;
    }


    /**
     * 退出房间
     *
     * @return
     */
    @Override
    public void quitRoom(String roomId) {
        Integer userId = UserThreadLocal.getUserId();
        Room room = (Room) redisUtil.get(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId);
        if (room == null) {
            throw new BusinessException(ErrorEnum.ROOM_NOT_EXIST);
        }

        //如果是房主直接关闭房间。好友也会退出
        if(userId.equals(room.getPlayerInfoList().get(0).getUserId())) {
            //删除缓存
            redisUtil.delete(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId);
            //私发给房间中的好友
            if(room.getPlayerInfoList().size() > 1){
                WsMessage<String> wsMessage = new WsMessage<>();
                wsMessage.setEvent(WsEventConstants.ROOM_CLOSED_BY_OWNER_EVENT);
                wsMessage.setSenderId(userId);
                wsMessage.setTimestamp(System.currentTimeMillis());
                webSocketHandler.sendMessageToUser(room.getPlayerInfoList().get(1).getUserId(), wsMessage);
                //更新好友状态为在线
                redisUtil.set(RedisKeyConstants.REDIS_USER_STATUS_KEY_PREFIX + room.getPlayerInfoList().get(1).getUserId(), UserOnlineStatus.ONLINE.getDesc());
            }
        }else{
            //不是房主，直接移除玩家
            room.getPlayerInfoList().remove(1);
            room.setStatus(RoomStatusEnums.WAITING.getDesc());

            //更新缓存
            redisUtil.set(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId, room);
        }
        //更新用户状态
        redisUtil.set(RedisKeyConstants.REDIS_USER_STATUS_KEY_PREFIX + userId, UserOnlineStatus.ONLINE.getDesc());

        //广播给好友，改为在线状态
        WsMessage<String> wsMessage = new WsMessage<>();
        wsMessage.setEvent(WsEventConstants.USER_QUIT_ROOM_EVENT);
        wsMessage.setSenderId(userId);
        wsMessage.setTimestamp(System.currentTimeMillis());
        webSocketHandler.broadcastToFriends(userId, wsMessage);

    }


    @Override
    public void invite(FightInviteDTO fightInviteDTO) {
        Integer userId = UserThreadLocal.getUserId();
        Integer receiverId = fightInviteDTO.getReceiverId();
        String roomId = fightInviteDTO.getRoomId();
        Object roomObject = redisUtil.get(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId);

        //检查参数
        if (receiverId == null || roomId == null) {
            throw new BusinessException(ErrorEnum.PARAM_ERROR);
        }
        if( roomObject == null){
            throw new BusinessException(ErrorEnum.ROOM_NOT_EXIST);
        }

        Room room = (Room) redisUtil.get(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId);
        if(room.getPlayerInfoList().size()==2){
            throw new BusinessException(ErrorEnum.ROOM_FULL);
        }
        //不能邀请自己
        if (receiverId.equals(UserThreadLocal.getUserId())) {
            throw new BusinessException(ErrorEnum.CANNOT_INVITE_SELF);
        }

        //获取邀请方用户状态
        String friendGameStatus = (String) redisUtil.get(RedisKeyConstants.REDIS_USER_STATUS_KEY_PREFIX + receiverId);

        //判断好友是否在线
        if (!webSocketHandler.isOnline(receiverId)) {
            throw new BusinessException(ErrorEnum.FRIEND_NOT_ONLINE);
        }
        //判断好友是否在游戏中
        if (UserOnlineStatus.PLAYING.getDesc().equals(friendGameStatus)) {
            throw new BusinessException(ErrorEnum.FRIEND_IS_PLAYING);
        }
        //判断好友是否在房间中
        if (UserOnlineStatus.INROOM.getDesc().equals(friendGameStatus)) {
            throw new BusinessException(ErrorEnum.FRIEND_IS_INROOM);
        }

        //将邀请信息存到redis
        //invite:12345 -- > {12345:pending},{11111: pending} 过期时间为 5秒
        String inviteKey = RedisKeyConstants.REDIS_FIGHT_INVITE_KEY_PREFIX + roomId;

        //向好友发送邀请
        WsMessage<String> wsMessage = new WsMessage<>();
        wsMessage.setEvent(WsEventConstants.INVITE_EVENT);
        wsMessage.setSenderId(userId);
        wsMessage.setData(inviteKey);
        wsMessage.setTimestamp(System.currentTimeMillis());

        webSocketHandler.sendMessageToUser(receiverId, wsMessage);

        redisUtil.setex(inviteKey, roomId, 1, TimeUnit.MINUTES); // key: invite:<inviteKey> value: roomId
    }

    /**
     * 接受邀请
     *
     * @param acceptDTO
     */
    @Override
    public void accept(FightInviteAcceptDTO acceptDTO) {
        String inviteKey = acceptDTO.getInviteKey();
        Integer senderId = acceptDTO.getSenderId();
        String roomId = (String) redisUtil.get(inviteKey);
        //判断邀请请求是否已经过期
        if (roomId == null) {
            throw new BusinessException(ErrorEnum.FIGHT_INVITE_EXPIRED);
        }

        //判断房间是否存在
        Room room = (Room) redisUtil.get(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId);
        if (room == null) {
            throw new BusinessException(ErrorEnum.ROOM_NOT_EXIST);
        }

        //判断房间是否已满
        if (RoomStatusEnums.FULL.getDesc().equals(room.getStatus())) {
            throw new BusinessException(ErrorEnum.ROOM_ALREADY_FULL);
        }
        //判断房间是否已经在游戏中
        if (RoomStatusEnums.PLAYING.getDesc().equals(room.getStatus())) {
            throw new BusinessException(ErrorEnum.ROOM_GAME_ALREADY_STARTED);
        }

        //加入房间
        Integer userId = UserThreadLocal.getUserId();
        PlayerInfoVO playerInfoVO = getPlayerInfo(userId);
        playerInfoVO.setIfRoomMaster(false);

        room.getPlayerInfoList().add(playerInfoVO);
        room.setStatus(RoomStatusEnums.FULL.getDesc());

        //更新缓存中房间的信息
        redisUtil.set(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId, room);
        //更新用户的状态为组队中
        redisUtil.set(RedisKeyConstants.REDIS_USER_STATUS_KEY_PREFIX + userId, UserOnlineStatus.INROOM.getDesc());

        //通知双方
        WsMessage<Room> wsMessage = new WsMessage();
        wsMessage.setEvent(WsEventConstants.INVITE_ACCEPT_EVENT);
        wsMessage.setData(room);
        wsMessage.setSenderId(-1);
        wsMessage.setTimestamp(System.currentTimeMillis());

        webSocketHandler.sendMessageToUser(userId,wsMessage);
        webSocketHandler.broadcastToFriends(userId,wsMessage);
    }

    /**
     * 玩家准备
     * @param readyDTO
     */
    @Override
    public void ready(PlayerReadyDTO readyDTO) {
        Integer userId = UserThreadLocal.getUserId();
        String roomId = readyDTO.getRoomId();
        Integer status = readyDTO.getStatus();
        if(roomId == null){
            throw new BusinessException(ErrorEnum.PARAM_ERROR);
        }
        if(!RoomPlayerStatus.READY.getCode().equals(status) && !RoomPlayerStatus.UNREADY.getCode().equals(status)){
            throw new BusinessException(ErrorEnum.PARAM_ERROR);
        }
        Room room = (Room) redisUtil.get(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId);
        if(room == null){
            throw new BusinessException(ErrorEnum.ROOM_NOT_EXIST);
        }

        //判断用户是否在该房间
        if(!room.getPlayerInfoList().stream().anyMatch(playerInfoVO -> playerInfoVO.getUserId().equals(userId))){
            throw new BusinessException(ErrorEnum.USER_NOT_IN_ROOM);
        }

        //更新准备状态
        room.getPlayerInfoList().get(1).setReadyStatus(status);
        redisUtil.set(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId, room);

        //通知房主
        WsMessage wsMessage = new WsMessage();
        wsMessage.setEvent(status.equals(RoomPlayerStatus.READY.getCode()) ? WsEventConstants.PLAYER_READY_EVENT : WsEventConstants.PLAYER_CANCEL_READY_EVENT);
        wsMessage.setSenderId(userId);
        wsMessage.setTimestamp(System.currentTimeMillis());
        webSocketHandler.sendMessageToUser(room.getPlayerInfoList().get(0).getUserId(), wsMessage);
    }



    /**
     * 开始游戏
     *
     * @param roomId
     */
    @Override
    public void startGame(String roomId) {
        //校验参数
        if (StringUtils.isEmpty(roomId)) {
            throw new BusinessException(ErrorEnum.PARAM_ERROR);
        }
        Room room = (Room) redisUtil.get(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId);
        if(room.getPlayerInfoList().size()<2){
            throw new BusinessException(ErrorEnum.FRIEND_NOT_IN_ROOM);
        }

        //初始化游戏
        room.initGame();
        room.setStatus(RoomStatusEnums.PLAYING.getDesc());
        //更新redis
        redisUtil.set(RedisKeyConstants.REDIS_ROOM_KEY_PREFIX + roomId, room);
        //插入一个数据，记录游戏步数数据
        redisUtil.set(RedisKeyConstants.REDIS_GAME_DATA_KEY_PREFIX + roomId,"");
        //为房间的用户存一个roomId
        redisUtil.set(RedisKeyConstants.REDIS_USERS_ROOM_ID_KEY_PREFIX + room.getPlayerInfoList().get(0).getUserId() , roomId);
        redisUtil.set(RedisKeyConstants.REDIS_USERS_ROOM_ID_KEY_PREFIX + room.getPlayerInfoList().get(1).getUserId() , roomId);

        //通知玩家
        WsMessage<Room> wsMessage = new WsMessage();
        wsMessage.setSenderId(-1);
        wsMessage.setEvent(WsEventConstants.GAME_STARTED_EVENT);
        wsMessage.setData(room);
        wsMessage.setTimestamp(System.currentTimeMillis());

        webSocketHandler.sendMessageToUser(room.getPlayerInfoList().get(0).getUserId(), wsMessage);
        webSocketHandler.sendMessageToUser(room.getPlayerInfoList().get(1).getUserId(), wsMessage);

        webSocketHandler.broadcastToFriendsUpdateUserStatus(room.getPlayerInfoList().get(0).getUserId());
        webSocketHandler.broadcastToFriendsUpdateUserStatus(room.getPlayerInfoList().get(1).getUserId());

    }


    /**
     * 获取玩家信息
     * @param userId
     * @return
     */
    private PlayerInfoVO getPlayerInfo(Integer userId){
        //查询缓存
        PlayerInfoVO playerInfoVO = (PlayerInfoVO) redisUtil.get(RedisKeyConstants.REDIS_PLAYER_INFO_KEY_PREFIX + userId);
        if(playerInfoVO == null){
            //查询数据库用户信息
            UserInfoVO userInfoVO = userMapper.getUserInfoById(userId);

            playerInfoVO = new PlayerInfoVO();
            playerInfoVO.setUserId(userId);
            playerInfoVO.setUsername(userInfoVO.getUsername());
            playerInfoVO.setAvatarUrl(userInfoVO.getAvatarUrl());
            playerInfoVO.setReadyStatus(RoomPlayerStatus.UNREADY.getCode());
            //缓存玩家信息
            redisUtil.setex(RedisKeyConstants.REDIS_PLAYER_INFO_KEY_PREFIX + userId,playerInfoVO, 30, TimeUnit.MINUTES);
        }
        return playerInfoVO;
    }

}
